package com.k8.common.factroy;

import com.k8.common.aop.ProxyFactory;
import com.k8.common.aop.SmartInstantiationBeanPostProcessor;
import com.k8.common.aop.SpecialBeanProxyFactory;
import com.k8.common.factroy.instance.InstanceStrategyFactory;
import com.k8.common.factroy.loader.ExtensionDirector;
import com.k8.common.factroy.loader.ExtensionLoader;
import com.k8.common.factroy.loader.LoaderBeanInstanceStrategyFactory;
import com.k8.common.ioc.BeanPostProcessor;
import com.k8.common.ioc.Injector;
import com.k8.common.ioc.injectorImpl.InjectorWrapper;
import com.k8.common.ioc.processorImpl.BeanFactoryAwarePostProcessor;
import com.k8.common.ioc.processorImpl.ExtensionAccessorAwarePostProcessor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.LinkedList;
import java.util.List;


/*
 * 区别于du的ScopeModel，由BeanFactory来同一管理容器和扩展容器
 * private final List<BeanInfo> registeredBeanInfos = new CopyOnWriteArrayList<>();
 * du 的 BeanFactory 注册Bean的时候通过一个registerBeanInfos来保存相同类型的
 * */


@Slf4j
public class BeanFactory implements InstanceStrategyFactory {
    private final static String bean_factory_injector = "beanFactoryInjector";
    private final static String spi_injector = "spiInjector";
    @Getter
    ExtensionDirector extensionDirector;

    public BeanFactory(String proxyType) {
        initialize(proxyType);
    }

    @Getter
    InstanceStrategyFactory instanceStrategyFactory;

    //环境配置 todo
    private void initialize(String proxyType) {
        InstanceStrategyFactory directorInstanceStrategyFac = new LoaderBeanInstanceStrategyFactory();
        //ExtensionDirector新建时会获取手动注入的BeanPostProcessor，但是beanPostProcessor的管理需要instanceStrategy
        //故此将BeanPostProcessor的生成放到Director中
        this.extensionDirector = new ExtensionDirector(directorInstanceStrategyFac);


        List<BeanPostProcessor> beanPostProcessors = extensionDirector.getBeanPostProcessors();
        //获取injector需要先注入BeanFactoryAwarePostProcessor，保证使BeanFactoryInjector能获取到BeanFactory
        beanPostProcessors.add(new BeanFactoryAwarePostProcessor(this));
        //确保SpiInjector生效
        beanPostProcessors.add(new ExtensionAccessorAwarePostProcessor(extensionDirector));

        //beanPostProcessor不支持依赖注入，此时它还未生成injector
        ExtensionLoader<Injector> injectorExtensionLoader = extensionDirector.getExtensionLoader(Injector.class);
        List<Injector> injectors = injectorExtensionLoader.getExtensions();
        InjectorWrapper factoryInjectorWrapper = new InjectorWrapper();
        Injector factoryInjector = injectorExtensionLoader.getExtension(bean_factory_injector);
        injectors.remove(factoryInjector);
        //将BeanFactoryInjector排到首位
        injectors.add(0, factoryInjector);
        factoryInjectorWrapper.setInjectors(injectors);

        Injector spiInjector = injectorExtensionLoader.getExtension(spi_injector);
        List<Injector> injectors1 = new LinkedList<>();
        for (Injector injector : injectors) {
            if (injector != spiInjector) {
                injectors1.add(injector);
            }
        }
        //将spiInjector放到首位
        injectors1.add(0, spiInjector);
        InjectorWrapper spiInjectorWrapper = new InjectorWrapper();
        spiInjectorWrapper.setInjectors(injectors1);
        //在扫描AspectjConfig配置类之前注入Injector，使得AspectConfig接口的aop实现类也能进行注入,injector不支持动态代理
        directorInstanceStrategyFac.setInjector(spiInjectorWrapper);


        //开始初始化BeanFactory
        this.instanceStrategyFactory = new BeanFactoryInstanceStrategyFactory();
        this.instanceStrategyFactory.setInjector(factoryInjectorWrapper);
        this.instanceStrategyFactory.setBeanPostProcessors(beanPostProcessors);
        //保证aop实现类在依赖注入时可以获取到BeanFactory的实例工厂中的bean，此时实例工厂已经初始化好了
        //创建Smart时会扫描AspectjConfig实现类
        //todo 将动态代理配置不能写死,全局配置指定，不同于url的动态代理指定
        SmartInstantiationBeanPostProcessor smartProcessor = new SmartInstantiationBeanPostProcessor(proxyType);
        beanPostProcessors.add(smartProcessor);
        //先添加再扫描，保证Aspect实现类中的依赖注入的实例能够被准确动态代理
        smartProcessor.scanAspectjImpl(extensionDirector);
        SpecialBeanProxyFactory specialBeanProxyFactory = new SpecialBeanProxyFactory(extensionDirector.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension());
        //specialBeanProxyFactory用于处理不属于BeanFactory或ExtensionAccessor管理的对象的实例化的动态代理
        specialBeanProxyFactory.scanAspectjImpl(extensionDirector);
        registryBean(specialBeanProxyFactory);
    }

    @Override
    public void registryBean(Object instance) {
        this.instanceStrategyFactory.registryBean(instance);
    }

    @Override
    public void registryBean(Object instance, String name) {
        this.instanceStrategyFactory.registryBean(instance, name);
    }

    @Override
    public <T> T getBean(Class<T> clazz) {
        return this.instanceStrategyFactory.getBean(clazz);
    }

    @Override
    public <T> T getBean(Class<T> clazz, String name) {
        return this.instanceStrategyFactory.getBean(clazz, name);
    }

    @Override
    public <T> T getOrRegistryBean(Class<T> clazz) {
        return this.instanceStrategyFactory.getOrRegistryBean(clazz);
    }

    @Override
    public <T> T getOrRegistryBean(Class<T> clazz, String name) {
        return this.instanceStrategyFactory.getOrRegistryBean(clazz, name);
    }

    @Override
    public <T> T getBeanOrRegistryToInject(Class<T> clazz) {
        return this.instanceStrategyFactory.getBeanOrRegistryToInject(clazz);
    }

    @Override
    public <T> T getBeanOrRegistryToInject(Class<T> clazz, String name) {
        return this.instanceStrategyFactory.getBeanOrRegistryToInject(clazz, name);
    }

    @Override
    public void setInjector(Injector injector) {
    }

    @Override
    public void setBeanPostProcessors(List<BeanPostProcessor> beanPostProcessors) {
    }

    @Override
    public BeanInfo getBeanInfo(String relyBeanName) {
        return this.instanceStrategyFactory.getBeanInfo(relyBeanName);
    }

    @Override
    public <T> List<T> getAllBeans(Class<T> tClass) {
        return this.instanceStrategyFactory.getAllBeans(tClass);
    }

    @Override
    public <E> E createProtoBean(Class<E> clazz) {
        return this.instanceStrategyFactory.createProtoBean(clazz);
    }
}
